Jx.declare('Jx.code.Operation', {

	$extend: 'Jx.lib.Operation',

	$actions: [

		'evaluateParser',

		'createSymbol',


		'generateVariable',

		'updateVariable',

		'declareVariable'

	],

	$helpers: {

		'Str': 'Jx.lib.String'

	},

	source: '',

	codes: undefined,

	params: undefined,

	symbolNamePrefix: 's',

	symbolGen: 0,

	symbolIndex: undefined,

	symbols: undefined,

	variableGen: 0,

	variables: undefined,

	variableIndex: undefined,

	variablePrefix: 'v',

	__callback_SortSymbol: function(a, b){

		var me = arguments.callee.scope,

			o = 'order';

		return (me.getSymbolValue(a, o) || 0) - (me.getSymbolValue(b, o) || 0);

	},

	constructor: function(){

		this.codes = [];

		this.params = [];

		this.symbolIndex = {};

		this.symbols = [];

		this.variables = [];

		this.$super(arguments);

	},

	onConstruct: function(code){

		if (Jx.isString(code)){

			this.source = code;

		}

		this.$super(arguments);

	},

	onDestroy: function(){

		var vars = this.variables,

			index = this.variableIndex;

		for(var l = vars.length;l--;){

			Jx.clearObject(index[ vars[l] ]);

		}

		Jx.destruct( this.codes, this.params, vars, index );

		this.$super(arguments);

	},

	allowEvaluateParser: function(o, parser){

		o.valid = Jx.instanceOf(parser, 'Jx.code.Parser');

	},

	onEvaluateParser: function(o, parser){

		if (parser.failed){

			var tokens = parser.tokens,

				len = tokens.length,

				start = len > 4 ?

					tokens[len - 4].from

					:

					0;

			if (len){

				this.setError('Failed Parsing near: ' + parser.tokenizer.input.substring(start, tokens[len -1].to));

			} else {

				this.setError('Failed Parsing near start of the code');

			}

		}

		if (!this.hasErrors()){

			this.setSuccessful();

		}

		if (parser.completed){

			this.setCompleted();

		}

	},

	allowCreateSymbol: function(o, expression){

		var J = Jx,

			symbol = {

				returns: false,

				expression: '',

				assign: false,

				order: 0

			};

		if (J.isString(expression)){

			symbol.expression = expression;

		} else if (J.isObject(expression)){

			J.assign(symbol, expression);

		}

		o.valid = symbol.expression.length > 0;

		o.params[1] = symbol;

		o.returnValue = void(0);

	},

	onCreateSymbol: function(o, symbol){

		var name = o.returnValue = this.symbolNamePrefix + (++this.symbolGen),

			symbols = this.symbols;


		// append expression
		symbol.raw = symbol.expression;

		if (symbol.assign){

			symbol.expression = name+' = '+symbol.raw;

		}

		this.symbolIndex[ name ] = symbol;

		symbols[ symbols.length ] = symbol.name = name;

	},

	getSymbol: function(name){

		var index = this.symbolIndex;

		return name && name in index ?

			index[name]

			:

			void(0);

	},

	getSymbolValue: function(name, attr){

		var symbol = this.getSymbol(name);

		attr = attr || "expression";

		return symbol && attr in symbol ?

			symbol[attr]

			:

			void(0);

	},

	sortSymbols: function(names){

		var fn = this.__callback_SortSymbol;

		if (Jx.isArray(names)){

			fn.scope = this;

			names.sort(fn);

		}

		return names;

	},

	joinSymbols: function(names, glue, attr){

		var codes = [];

		attr = attr || "expression";

		if (Jx.isArray(names)){

			for(var c=-1, l = names.length; l--;){

				codes[++c] = this.getSymbolValue(names[c], attr) || '';

			}

		}

		return codes.join( glue );

	}






});
